<?php
/*****************************************************************************
Copyright © 2008 The Regents of the University of Nevada
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
   derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/

class Preference {

	private $sxml;					// SimpleXMLObject representation of config file
	private static $instance;	// self object


	private static $affirmatives = array('true', 'yes', 'enabled',
		'active', 'on', '1');


	protected function __construct() {
		$fp = $_SERVER['DOCUMENT_ROOT'] . '/'
			. trim(Config::ISIS_FOLDER_ROOT, '/') . '/config.xml.php';
		if (!$this->sxml = @simplexml_load_file($fp)) {
			throw new PreferencesException(String::MISSING_CONFIGXML_FILE);
		}
	}


	public function __sleep() {}
	public function __wakeup() {}


	public static final function getInstance() {
		if (!self::$instance instanceof self) {
			self::$instance = new self;
		}
		return self::$instance;
	}


	public final function save() {
		$result = @$this->sxml->asXML($_SERVER['DOCUMENT_ROOT'] . '/'
			. trim(Config::ISIS_FOLDER_ROOT, '/') . '/config.xml.php');
		if (!$result) throw new PreferencesException(String::SAVE_PREFS_FAILED);
		return $result;
	}


	public final function getAllFields() {
		$result = $this->sxml->xpath('/isis/results/displayFields/field');
		$fields = array();
		foreach ($result as $r) {
			$field = new Field((string) $r->attributes()->mapping);
			$field->setName((string) $r->name);
			$fields[] = $field;
		}
		return $fields;
	}


	public final function getDBEngine() {
		$result = $this->sxml->xpath('/isis/dataStore/database/engine');
		return (string) $result[0];
	}


	public final function setDBEngine($str) {
		if (in_array($str, RDBMSDataStore::getAvailableDBMS())) {
			$this->sxml->dataStore->database->engine
				= String::xmlentities($str);
		}
		else {
			throw new InputException(String::INVALID_DBMS);
			return false;
		}
	}


	public final function getDBHost() {
		$result = $this->sxml->xpath('/isis/dataStore/database/host');
		return (string) $result[0];
	}


	public final function setDBHost($str) {
		$this->sxml->dataStore->database->host = String::xmlentities($str);
	}


	public final function getDBName() {
		$result = $this->sxml->xpath('/isis/dataStore/database/name');
		return (string) $result[0];
	}


	public final function setDBName($str) {
		$this->sxml->dataStore->database->name = String::xmlentities($str);
	}


	public final function getDBPass() {
		$result = $this->sxml->xpath('/isis/dataStore/database/password');
		return (string) $result[0];
	}


	public final function setDBPass($str) {
		$this->sxml->dataStore->database->password
			= String::xmlentities($str);
	}


	public final function getDBPort() {
		$result = $this->sxml->xpath('/isis/dataStore/database/port');
		return (string) $result[0];
	}


	public final function setDBPort($int) {
		$int = ((int) $int > 0) ? (int) substr($int, 0, 5) : null;
		$this->sxml->dataStore->database->port = $int;
	}


	public final function getDBUser() {
		$result = $this->sxml->xpath('/isis/dataStore/database/username');
		return (string) $result[0];
	}


	public final function setDBUser($str) {
		$this->sxml->dataStore->database->username
			= String::xmlentities($str);
	}


	public final function isDebugMode() {
		$result = $this->sxml->xpath('/isis/debugMode');
		return (in_array($result[0], self::$affirmatives)) ? true : false;
	}


	public final function setDebugMode($bool) {
		$this->sxml->debugMode = ($bool) ? 'true' : 'false';
	}


	public final function getDefaultFrameHeight() {
		$result = $this->sxml->xpath('/isis/map/defaultFrameH');
		return (float) $result[0];
	}


	public final function setDefaultFrameHeight($float) {
		$this->sxml->map->defaultFrameH = (float) $float;
	}


	public final function getDefaultFrameLatN() {
		$result = $this->sxml->xpath('/isis/map/defaultFrameLatN');
		return (float) $result[0];
	}


	public final function setDefaultFrameLatN($float) {
		$this->sxml->map->defaultFrameLatN = (float) $float;
	}


	public final function getDefaultFrameLatS() {
		$lat_n = $this->sxml->xpath('/isis/map/defaultFrameLatN');
		return (float) $lat_n[0] - (float) $this->getDefaultFrameHeight();
	}


	public final function getDefaultFrameLongE() {
		$long_w = $this->sxml->xpath('/isis/map/defaultFrameLongW');
		return (float) $long_w[0] + $this->getDefaultFrameWidth();
	}


	public final function getDefaultFrameLongW() {
		$result = $this->sxml->xpath('/isis/map/defaultFrameLongW');
		return (float) $result[0];
	}


	public final function setDefaultFrameLongW($float) {
		$this->sxml->map->defaultFrameLongW = (float) $float;
	}


	public final function getDefaultFrameWidth() {
		$result = $this->sxml->xpath('/isis/map/defaultFrameW');
		return (float) $result[0];
	}


	public final function setDefaultFrameWidth($float) {
		$this->sxml->map->defaultFrameW = (float) $float;
	}


	public final function getDefaultFrameX() {
		return $this->getDefaultFrameLongW();
	}


	public final function getDefaultFrameY() {
		return -$this->getDefaultFrameLatN();
	}


	public final function getDefaultMapViewbox() {
		return implode(' ', array(
			$this->getMapLongW(),
			-$this->getMapLatN(),
			$this->getMapLongE() - $this->getMapLongW(),
			$this->getMapLatN() - $this->getMapLatS()
		));
	}


	public final function getDefaultViewViewBox() {
		return implode(' ', array(
			$this->getInitialViewLongW(),
			-$this->getInitialViewLatN(),
			$this->getInitialViewLongE() - $this->getInitialViewLongW(),
			$this->getInitialViewLatN() - $this->getInitialViewLatS()
		));
	}


	public final function getDisplayFields() {
		$result = $this->sxml->xpath('/isis/results/displayFields/field');
		$fields = array();
		foreach ($result as $r) {
			if (!in_array((string) $r->display, self::$affirmatives)) continue;
			$field = new Field((string) $r->attributes()->mapping);
			$field->setName((string) $r->name);
			$fields[] = $field;
		}
		return $fields;
	}


	public final function shouldEnforcePanMapBounds() {
		$result = $this->sxml->xpath('/isis/map/enforceBounds');
		return (in_array($result[0], self::$affirmatives)) ? true : false;
	}


	public final function setEnforcePanMapBounds($bool) {
		$bool = ($bool) ? 'true' : 'false';
		$this->sxml->map->enforceBounds = $bool;
	}


	public final function setFieldDisplay(array $fields) { // mapping => bool
		foreach ($fields as $mapping => $display) {
			$xpath = sprintf("results/displayFields/field[@mapping = '%s']",
				$mapping
			);
			$node = $this->sxml->xpath($xpath);
			$node[0]->display = ($display) ? 'true' : 'false';
		}
	}


	public final function setFieldNames(array $fields) { // mapping => name
		foreach ($fields as $mapping => $name) {
			$xpath = sprintf("results/displayFields/field[@mapping = '%s']",
				$mapping
			);
			$node = $this->sxml->xpath($xpath);
			$node[0]->name = String::xmlentities($name);
		}
	}


	public final function getGridRows() {
		$result = $this->sxml->xpath('/isis/results/gridRows');
		return (int) $result[0];
	}


	public final function setGridRows($int) {
		$this->sxml->results->gridRows = (int) $int;
	}


	public final function getGridMaxWords() {
		$result = $this->sxml->xpath('/isis/results/gridMaxWords');
		return (int) $result[0];
	}


	public final function setGridMaxWords($int) {
		$this->sxml->results->gridMaxWords = (int) substr($int, 0, 2);
	}


	public final function getInitialViewHeight() {
		return $this->getInitialViewLatN() - $this->getInitialViewLatS();
	}


	public final function getInitialViewLatN() {
		$result = $this->sxml->xpath('/isis/map/initialView/latNorth');
		return (float) $result[0];
	}


	public final function setInitialViewLatN($float) {
		$this->sxml->map->initialView->latNorth = (float) $float;
	}


	public final function getInitialViewLatS() {
		$result = $this->sxml->xpath('/isis/map/initialView/latSouth');
		return (float) $result[0];
	}


	public final function setInitialViewLatS($float) {
		$this->sxml->map->initialView->latSouth = (float) $float;
	}


	public final function getInitialViewLongE() {
		$result = $this->sxml->xpath('/isis/map/initialView/longEast');
		return (float) $result[0];
	}


	public final function setInitialViewLongE($float) {
		$this->sxml->map->initialView->longEast = (float) $float;
	}


	public final function getInitialViewLongW() {
		$result = $this->sxml->xpath('/isis/map/initialView/longWest');
		return (float) $result[0];
	}


	public final function setInitialViewLongW($float) {
		$this->sxml->map->initialView->longWest = (float) $float;
	}


	public final function getInitialViewViewBox() {
		return $this->getInitialViewX() . ' '
			. $this->getInitialViewY() . ' '
			. $this->getInitialViewWidth() . ' '
			. $this->getInitialViewHeight();
	}


	public final function getInitialViewWidth() {
		return $this->getInitialViewLongE() - $this->getInitialViewLongW();
	}


	public final function getInitialViewX() {
		return $this->getInitialViewLongW();
	}


	public final function getInitialViewY() {
		return -$this->getInitialViewLatN();
	}


	public final function loginIsValid($username, $password) {
		$xpath = sprintf(Query::USER_LOGIN_XPATH,
			String::paranoid($username, array('-', '_', '.')),
			sha1($password)
		);
		$result = $this->sxml->xpath($xpath);
		if ($result[0] instanceof SimpleXMLElement) {
			return true;
		}
		return false;
	}


	public final function getMapHeight() {
		return $this->getMapLatN() - $this->getMapLatS();
	}


	public final function getMapLatN() {
		$result = $this->sxml->xpath('/isis/map/latNorth');
		return (float) $result[0];
	}


	public final function setMapLatN($float) {
		$this->sxml->map->latNorth = (float) $float;
	}


	public final function getMapLatS() {
		$result = $this->sxml->xpath('/isis/map/latSouth');
		return (float) $result[0];
	}


	public final function setMapLatS($float) {
		$this->sxml->map->latSouth = (float) $float;
	}


	public final function getMapLongE() {
		$result = $this->sxml->xpath('/isis/map/longEast');
		return (float) $result[0];
	}


	public final function setMapLongE($float) {
		$this->sxml->map->longEast = (float) $float;
	}


	public final function getMapLongW() {
		$result = $this->sxml->xpath('/isis/map/longWest');
		return (float) $result[0];
	}


	public final function setMapLongW($float) {
		$this->sxml->map->longWest = (float) $float;
	}


	public final function getMapMinCoverageEW() {
		$result = $this->sxml->xpath('/isis/map/minCoverage/ew');
		return (float) $result[0];
	}


	public final function setMapMinCoverageEW($float) {
		$this->sxml->map->minCoverage->ew = (float) $float;
	}


	public final function getMapMinCoverageNS() {
		$result = $this->sxml->xpath('/isis/map/minCoverage/ns');
		return (float) $result[0];
	}


	public final function setMapMinCoverageNS($float) {
		$this->sxml->map->minCoverage->ns = (float) $float;
	}


	public final function getMapWidth() {
		return $this->getMapLongE() - $this->getMapLongW();
	}


	public final function getMapX() {
		return $this->getMapLongW();
	}


	public final function getMapY() {
		return -$this->getMapLatN();
	}


	public final function getNameForField(Field $f) {
		$q = sprintf('/isis/results/displayFields/field[@mapping = "%s"]/name',
			String::paranoid($f->getMapping(), array(':'))
		);
		$result = $this->sxml->xpath($q);
		return (string) $result[0];
	}


	public final function getNumMapCoordinateDigits() {
		$result = $this->sxml->xpath('/isis/map/coordinateDigits');
		return (int) $result[0];
	}


	public final function setNumMapCoordinateDigits($int) {
		$this->sxml->map->coordinateDigits = (int) substr($int, 0, 1);
	}


	public final function getPanFactor() {
		$result = $this->sxml->xpath('/isis/map/panFactor');
		return (float) $result[0];
	}


	public final function setPanFactor($float) {
		$this->sxml->map->panFactor = (float) $float;
	}


	public final function getResultsMapOutlinesLimit() {
		$result = $this->sxml->xpath('/isis/results/mapOutlinesLimit');
		return (int) $result[0];
	}


	public final function setResultsMapOutlinesLimit($int) {
		$this->sxml->results->mapOutlinesLimit = (int) $int;
	}


	public final function getResizerThickness() {
		$result = $this->sxml->xpath('/isis/map/resizerThickness');
		return (float) $result[0];
	}


	public final function setResizerThickness($float) {
		$this->sxml->map->resizerThickness = (float) $float;
	}


	public final function getScaleFactor() {
		$result = $this->sxml->xpath('/isis/map/scaleFactor');
		return (float) $result[0];
	}


	public final function setScaleFactor($float) {
		$this->sxml->map->scaleFactor = (float) $float;
	}


	public final function getStorageEngine() {
		$result = $this->sxml->xpath('/isis/dataStore/engine');
		return (string) $result[0];
	}


	public final function setStorageEngine($str) {
		if (in_array($str, DataStore::getAvailableEngines())) {
			$this->sxml->dataStore->engine = String::xmlentities($str);
		}
		else {
			throw new InputException(String::INVALID_STORAGE_ENGINE);
			return false;
		}
	}


	public final function getTileColumns() {
		$result = $this->sxml->xpath('/isis/results/tileCols');
		return (int) $result[0];
	}


	public final function setTileColumns($int) {
		$this->sxml->results->tileCols = (int) $int;
	}


	public final function getTileMaxWords() {
		$result = $this->sxml->xpath('/isis/results/tileMaxWords');
		return (int) $result[0];
	}


	public final function setTileMaxWords($int) {
		$this->sxml->results->tileMaxWords = (int) substr($int, 0, 2);
	}


	public final function getTileRows() {
		$result = $this->sxml->xpath('/isis/results/tileRows');
		return (int) $result[0];
	}


	public final function setTileRows($int) {
		$this->sxml->results->tileRows = (int) $int;
	}


	public final function getVersion() {
		return '20090112';
	}

} // Preference

?>
